/**
 * Created by Qingger Corp.
 * User: jsspf
 * Date: 2017/11/4
 * Time: 13:59
 */

/// 最基本接口
interface LabelledValue {
    label : string
}
function printLabel(labelledObj : LabelledValue) {
    console.log(labelledObj.label);
}


/// 接口可选属性
interface SquareConfig {
    color? : string;
    width? : number;
    [propName:string] : any  // 还会带有任意数量的其它属性
}
function createSquare(config:SquareConfig) : SquareConfig {
   let newSquare =  {color: "white", area: 100};
    if (config.color) {
        newSquare.color = config.color;
    }
    if (config.width) {
        newSquare.area = config.width * config.width;
    }
    return newSquare;
}
let mySquare = createSquare({color: "black"});
console.log(mySquare);

let mySquare2 = createSquare({"color":"red","width":100,"height":30});


/// 只读属性
interface Point {
    readonly x : number;
    readonly y : number
}

let p1 : Point = {x:10,y:20};
// p1.x = 100;  // Error

let a : number[] = [1,2,3,4];
let ro : ReadonlyArray<number> = a;
let b : number[] = ro as number[];

ro.forEach(function(value:number,key:number){
   console.log(value+key);
});


/// 函数类型
// 接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外，接口也可以描述函数类型。
// 为了使用接口表示函数类型，我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。

interface SearchFunc {
    (source:string,subString:string) : boolean;
}
// 定义后，我们可以像使用其它接口一样使用这个函数类型的接口
let mySearch : SearchFunc = function(source:string,subString:string) {
    let result = source.search(subString);
    return result > -1;
};


/// 可索引类型
// 与使用接口描述函数类型差不多，我们也可以描述那些能够“通过索引得到”的类型
// 可索引类型具有一个 索引签名，它描述了对象索引的类型，还有相应的索引返回值类型。
// 定义了StringArray接口，它具有索引签名。 这个索引签名表示了当用 number去索引StringArray时会得到string类型的返回值
// interface StringArray {
//     [index : number] : string
// }
let myArray : StringArray = ["bob",'Fred'];
let myStr : string = myArray[0];

class InAnimal {
    name:string
}
class Dog extends InAnimal {
    breed : string
}

// 共有支持两种索引签名：字符串和数字。 可以同时使用两种类型的索引，但是数字索引的返回值必须是字符串索引返回值类型的子类型。
// 这是因为当使用 number来索引时，JavaScript会将它转换成string然后再去索引对象。
// 也就是说用 100（一个number）去索引等同于使用"100"（一个string）去索引，因此两者需要保持一致。


/// 类接口
interface ClockInterface {
    currentTime : Date;
    setTime(d:Date) : void;
}

/**
 * 
 * @class Clock
 * @implements {ClockInterface}
 */
class Clock implements ClockInterface {
    currentTime : Date;
    constructor(h:number,m:number) {
    }

    /**
     * 设置时间  
     * @param {Date} d 
     * @memberof Clock
     */
    public setTime(d:Date): void {
        throw 'Not Implemented';
    }
}


/// 类静态部分与实例部分的区别
// 当你用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误
// 因为当一个类实现了一个接口时，只对其实例部分进行类型检查。 constructor存在于类的静态部分，所以不在检查的范围内
// 因此，我们应该直接操作类的静态部分
interface ClockConstructor {
    new (hour : number , minute : number) : ClockInterface;
}

class DigitalClock implements ClockInterface {
    private _currentTime: Date;
    public get currentTime(): Date {
        return this._currentTime;
    };
    public set currentTime(value: Date) {
        this._currentTime = value;
    };

    constructor(h:number, m:number) { }

    public setTime(d:Date): void {
        this._currentTime = d;
        console.log("DigitalClock settime");
    }
}

/**
 * 
 * @class AnalogClock
 * @implements {ClockInterface}
 */
class AnalogClock implements ClockInterface {
    private _currentTime: Date;
    public get currentTime(): Date {
        return this._currentTime;
    };
    public set currentTime(value: Date) {
        this._currentTime = value;
    };

    constructor(h:number, m:number) { }

    public setTime(d:Date): void {
        this._currentTime = d;
        console.log("AnalogClock settime");
    }
}

// 注意，使用的是构造器来创建对象，而构造器的类型为ClockConstructor
function createClock(ctor:ClockConstructor,hour:number,minute:number) : ClockInterface {
    return new ctor(hour,minute);
}


/// 接口的继承
interface Shape1 {
    color : string
}

interface PenStroke {
    penWidth : number;
}

interface Square1 extends Shape1,PenStroke {
    sideLength : number;
}

let square = <Square1>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

let square2 : Square1 = {
    color : "red",
    sideLength : 11,
    penWidth : 3.4
}


/// 混合类型
// 因为JavaScript其动态灵活的特点，有时你会希望一个对象可以同时具有上面提到的多种类型
// 比如说:一个对象可以同时做为函数和对象使用，并带有额外的属性。
interface Counter {
    // 此接口的实例本身为一个函数，且具有interval和reset属性
    (start:number) : string; 
    interval : number;
    reset() : void;           // 具名函数属性
}

function getCounter() : Counter {
   // Counter类型的对象
   let counter = function(start:number) {
       return "Length:"+start;
   } as Counter;
   // 强制转换也可以这样写
   // let counter = <Counter>function(start:number) { /*...*/ }

   // 且具有interval与reset属性
   counter.interval = 123;
   counter.reset = function() {};

   return counter;
}




/// 接口继承类
// 当接口继承了一个类类型时，它会继承类的成员但不包括其实现. 就好像接口声明了所有类中存在的成员，但并没有提供具体实现一样。
class Control {
    private state : any;
}

/**
 * 接口继承了类,此接口有state和select属性
 * @interface SelectableControl
 * @extends {Control}
 */
interface SelectableControl extends Control {
    select() : void;
}

// 不能直接实现SelectableControl,需要 extends Control implements SelectableControl
class ImageSelect extends Control implements SelectableControl {
    public select(): void {
        throw 'Not Implemented';
    }
}
